package jp.android_group.attender.android.net;

/*
 * Copyright (c) 2009, "Japan Android Group Attender Team"
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import jp.android_group.attender.android.entity.Attendee;
import jp.android_group.attender.android.entity.Event;
import jp.android_group.attender.android.entity.ServerEntry;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

/**
 * サーバーインターフェース まいむぞう氏のサーバー向け
 * 
 */
public class ServerInterface4Maimuzo implements IServerInterface {
	public static final String LOG_TAG = "ServerInterface4Maimuzo";
	public static final boolean DEBUG_FLG = false;

	private ServerEntry mServerEntry = null;

	public ServerInterface4Maimuzo(ServerEntry entry) {
		this.mServerEntry = entry;
	}

	public static final String URI_BASE_GAE = "http://attender.android-app.info";
	public static final String URI_GET_EVENT = "/events.xml";
	public static final String URI_GET_ATTENDEE_BASE = "/events/";
	public static final String URI_REGISTER_ATTENDEE_BASE = "/events/";

	/**
	 * イベントデータの取得
	 * 
	 * @param authData
	 * @return イベントリスト
	 * @throws HttpResponseException
	 * @throws IOException
	 */
	@Override
	public void getEvents(final SQLiteDatabase db)
			throws HttpResponseException, IOException {

		// ハンドラ
		DefaultHandler handler = new DefaultHandler() {
			Event event = new Event();
			private boolean eventsTagStarted = false;

			@Override
			public void startDocument() {
				db.beginTransaction();
			}

			@Override
			public void endDocument() {
				db.setTransactionSuccessful();
				db.endTransaction();
			}

			public void startElement(String uri, String localName,
					String qName, Attributes attributes) {

				if (eventsTagStarted && localName.equals("event")) {
					event = new Event();
					event.setEventCode(Integer.parseInt(attributes.getValue(
							"id").trim()));
					event.setName(attributes.getValue("name").trim());

					// 保存
					Event.save(event, db);

				} else if (localName.equals("events")) {
					eventsTagStarted = true;
				}
			}

			public void endElement(String uri, String localName, String qName) {
				if (localName.equals("events")) {
					eventsTagStarted = false;
				}
			}

		};

		InputStream in = httpConnect(new HttpGet(mServerEntry.getBaseUrl()
				+ URI_GET_EVENT));

		// XMLのパース
		parseXml(handler, in);

	}

	/**
	 * 出席予定者情報の取得
	 * 
	 * @param authData
	 * @param eventData
	 * @return 出席者リスト
	 * @throws HttpResponseException
	 * @throws IOException
	 */
	@Override
	public void getAttendees(final Event eventData, final SQLiteDatabase db)
			throws HttpResponseException, IOException {

		String uri = mServerEntry.getBaseUrl() + URI_GET_ATTENDEE_BASE
				+ eventData.getEventCode() + ".xml";

		InputStream in = httpConnect(new HttpGet(uri));

		// ハンドラ
		DefaultHandler handler = new DefaultHandler() {
			private Attendee tmp = new Attendee();

			@Override
			public void startDocument() {
				db.beginTransaction();
			}

			@Override
			public void endDocument() {
				db.setTransactionSuccessful();
				db.endTransaction();
			}

			public void startElement(String uri, String localName,
					String qName, Attributes attributes) {

				if (localName.equals("user")) {
					tmp.setEventId(eventData.getId());
					tmp.setAttendeeCode(attributes.getValue("id").trim());
					tmp.setName(attributes.getValue("name").trim());
					tmp.setMailAddress(attributes.getValue("mail").trim());
					tmp.setNumber(attributes.getValue("number").trim());
					tmp.setCameString(attributes.getValue("came").trim());
					// mTmpAttendee.setComment(attributes.getValue("comment").trim());
					tmp.setComment("コメントはありません");

					// DBに保存
					Attendee.save(tmp, db);

				}
			}
		};

		// XMLのパース
		parseXml(handler, in);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * jp.android_group.attender.android.net.IServerInterface#uploadAttendees
	 * (jp.android_group.attender.android.entity.Event, android.database.Cursor)
	 */
	@Override
	public void uploadAttendees(final Event eventData, Cursor cursor,
			final SQLiteDatabase db) throws HttpResponseException, IOException {

		String uri = mServerEntry.getBaseUrl() + URI_REGISTER_ATTENDEE_BASE
				+ eventData.getEventCode() + ".xml";

		List<NameValuePair> putContent = createPutContent(cursor);

		if (putContent.size() > 0) {

			HttpPut method = new HttpPut(uri);
			method.setEntity(new UrlEncodedFormEntity(putContent, HTTP.UTF_8));
			InputStream in = httpConnect(method);

			// ハンドラ
			DefaultHandler handler = new DefaultHandler() {
				private Attendee tmp = new Attendee();

				@Override
				public void startDocument() {
					db.beginTransaction();
				}

				@Override
				public void endDocument() {
					db.setTransactionSuccessful();
					db.endTransaction();
				}

				public void startElement(String uri, String localName,
						String qName, Attributes attributes) {

					if (localName.equals("user")) {
						tmp.setEventId(eventData.getId());
						tmp.setAttendeeCode(attributes.getValue("id").trim());
						tmp.setName(attributes.getValue("name").trim());
						tmp.setMailAddress(attributes.getValue("mail").trim());
						tmp.setNumber(attributes.getValue("number").trim());
						tmp.setCameString(attributes.getValue("came").trim());
						// mTmpAttendee.setComment(attributes.getValue("comment").trim());
						tmp.setComment("コメントはありません");

						// DBに保存
						Attendee.save(tmp, db);

					}
				}
			};

			// XMLのパース
			parseXml(handler, in);
		}
	}

	/**
	 * PUTする内容を構築
	 * 
	 * @param cursor
	 * @return
	 */
	private static List<NameValuePair> createPutContent(Cursor cursor) {
		final List<NameValuePair> list = new ArrayList<NameValuePair>();

		Attendee data = new Attendee();
		for (int num = 0; cursor.moveToNext(); num++) {
			Attendee.fetchData(cursor, data);
			if (data.getAttendFlg() == Attendee.ATTEND_FLG_ATTEND) {
				NameValuePair val = new BasicNameValuePair("userid[" + num
						+ "]", data.getAttendeeCode());
				list.add(val);
				num++;
			}

		}

		return list;
	}

	/**
	 * HttpConnect
	 * 
	 * @param url
	 * @param authData
	 * @throws IOException
	 * @throws HttpResponseException
	 * @return
	 */
	private InputStream httpConnect(HttpUriRequest urlRequest)
			throws IOException, HttpResponseException {

		InputStream result = null;

		DefaultHttpClient con = new DefaultHttpClient();

		con.getCredentialsProvider().setCredentials(
				new AuthScope(urlRequest.getURI().getHost(), urlRequest
						.getURI().getPort()),
				new UsernamePasswordCredentials(mServerEntry.getLoginId(),
						mServerEntry.getPassword()));

		HttpResponse res = con.execute(urlRequest);
		if (DEBUG_FLG)
			Log.e(LOG_TAG, "statusCode is "
					+ res.getStatusLine().getStatusCode());

		if (res.getStatusLine().getStatusCode() == 200) {

			HttpEntity entity = res.getEntity();
			result = entity.getContent();

		} else {
			throw new HttpResponseException(
					res.getStatusLine().getStatusCode(), res.getStatusLine()
							.getReasonPhrase());
		}

		return result;

	}

	/**
	 * ＸＭＬをパース
	 * 
	 * @param handler
	 * @param in
	 * @return
	 */
	private static boolean parseXml(DefaultHandler handler, InputStream in) {

		boolean result = true;

		// SAXパーサーファクトリを生成
		SAXParserFactory spfactory = null;

		// SAXパーサーを生成
		SAXParser parser = null;

		try {
			// SAXパーサーファクトリを取得
			spfactory = SAXParserFactory.newInstance();

			// SAXパーサーを生成
			parser = spfactory.newSAXParser();

			// XMLファイルを指定されたデフォルトハンドラーで処理
			parser.parse(in, handler);

		} catch (SAXException e) {
			if (DEBUG_FLG)
				Log.e(LOG_TAG, e.getMessage());
			result = false;

		} catch (ParserConfigurationException e) {
			if (DEBUG_FLG)
				Log.e(LOG_TAG, e.getMessage());
			result = false;

		} catch (IOException e) {
			if (DEBUG_FLG)
				Log.e(LOG_TAG, e.getMessage());
			result = false;
		}

		return result;
	}

	/**
	 * XMLハンドラ イベント情報
	 */
	@Deprecated
	private class GetEventDataHandler extends DefaultHandler {

		List<Event> mEventList = null;

		private Event mTmpEvent = null;
		private boolean eventsTagStarted = false;

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) {

			if (eventsTagStarted && localName.equals("event")) {
				mTmpEvent = new Event();
				mTmpEvent.setEventCode(Integer.parseInt(attributes.getValue(
						"id").trim()));
				mTmpEvent.setName(attributes.getValue("name").trim());
				mEventList.add(mTmpEvent);

			} else if (localName.equals("events")) {
				eventsTagStarted = true;
				mEventList = new LinkedList<Event>();
			}
		}

		public void endElement(String uri, String localName, String qName) {
			if (localName.equals("events")) {
				eventsTagStarted = false;
			}
		}
	}

	/**
	 * XMLハンドラ 出席予定者情報
	 */
	@Deprecated
	private class GetAttendeeListHandler extends DefaultHandler {

		HashMap<String, Attendee> mAttendeeList = null;
		private Attendee mTmpAttendee = null;
		private boolean usersTagStarted = false;

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) {

			if (usersTagStarted && localName.equals("user")) {
				mTmpAttendee = new Attendee();
				mTmpAttendee.setAttendeeCode(attributes.getValue("id").trim());
				mTmpAttendee.setName(attributes.getValue("name").trim());
				mTmpAttendee.setMailAddress(attributes.getValue("mail").trim());
				mTmpAttendee.setNumber(attributes.getValue("number").trim());
				mTmpAttendee.setCameString(attributes.getValue("came").trim());
				// mTmpAttendee.setComment(attributes.getValue("comment").trim());
				mTmpAttendee.setComment("コメントはありません");

				mAttendeeList.put(mTmpAttendee.getAttendeeCode(), mTmpAttendee);

				if (DEBUG_FLG)
					Log.v(LOG_TAG, "attendee is "
							+ attributes.getValue("came").trim());

			} else if (localName.equals("users")) {
				usersTagStarted = true;
				mAttendeeList = new HashMap<String, Attendee>();
			}
		}

		public void endElement(String uri, String localName, String qName) {
			if (localName.equals("events")) {
				usersTagStarted = false;
			}
		}
	}

	/**
	 * XMLハンドラ 参加者情報のアップロード結果
	 */
	@Deprecated
	private class UploadAttendeeHandler extends DefaultHandler {

		HashMap<String, Attendee> mAttendeeList = null;

		private Attendee mTmpAttendee = null;
		private boolean usersTagStarted = false;

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) {

			if (usersTagStarted && localName.equals("user")) {
				mTmpAttendee = new Attendee();
				mTmpAttendee.setAttendeeCode(attributes.getValue("id").trim());
				mTmpAttendee.setName(attributes.getValue("name").trim());
				mTmpAttendee.setMailAddress(attributes.getValue("mail").trim());
				mTmpAttendee.setNumber(attributes.getValue("number").trim());
				mTmpAttendee.setCameString(attributes.getValue("came").trim());
				// mTmpAttendee.setComment(attributes.getValue("comment").trim());
				mTmpAttendee.setComment("コメントはありません");

				if (mAttendeeList.containsKey(mTmpAttendee.getId()) == false) {
					mAttendeeList.put(mTmpAttendee.getAttendeeCode(),
							mTmpAttendee);
				} else {
					if (DEBUG_FLG)
						Log.v(LOG_TAG, "key " + mTmpAttendee.getId()
								+ " is already contained.");
				}

			} else if (localName.equals("users")) {
				usersTagStarted = true;
				mAttendeeList = new HashMap<String, Attendee>();
			}
		}

		public void endElement(String uri, String localName, String qName) {
			if (localName.equals("events")) {
				usersTagStarted = false;
			}
		}
	}

	/**
	 * イベントデータの取得
	 * 
	 * @param authData
	 * @return イベントリスト
	 * @throws HttpResponseException
	 * @throws IOException
	 */
	@Deprecated
	public List<Event> getEventData() throws HttpResponseException, IOException {

		List<Event> result = null;

		// ハンドラ
		GetEventDataHandler handler = new GetEventDataHandler();

		InputStream in = httpConnect(new HttpGet(mServerEntry.getBaseUrl()
				+ URI_GET_EVENT));

		if (parseXml(handler, in)) {
			result = handler.mEventList;
		}

		return result;

	}

	/**
	 * 出席予定者情報の取得
	 * 
	 * @param authData
	 * @param eventData
	 * @return 出席者リスト
	 * @throws HttpResponseException
	 * @throws IOException
	 */
	@Deprecated
	public HashMap<String, Attendee> getAttendeeData(Event eventData)
			throws HttpResponseException, IOException {

		HashMap<String, Attendee> result = null;

		String uri = mServerEntry.getBaseUrl() + URI_GET_ATTENDEE_BASE
				+ eventData.getEventCode() + ".xml";

		// ハンドラ
		GetAttendeeListHandler handler = new GetAttendeeListHandler();

		InputStream in = httpConnect(new HttpGet(uri));

		if (parseXml(handler, in)) {
			result = handler.mAttendeeList;
		}

		return result;
	}

	/**
	 * PUTする内容を構築
	 * 
	 * @param attendeeDataList
	 * @return
	 */
	@Deprecated
	private static List<NameValuePair> createPutContent(
			HashMap<String, Attendee> attendeeDataList) {

		final List<NameValuePair> list = new ArrayList<NameValuePair>();

		int num = 0;
		for (Attendee data : attendeeDataList.values()) {
			if (data.getAttendFlg() == Attendee.ATTEND_FLG_ATTEND) {
				NameValuePair val = new BasicNameValuePair("userid[" + num
						+ "]", data.getAttendeeCode());
				list.add(val);
				num++;
			}
		}

		return list;
	}

	/**
	 * 参加者情報のアップロード
	 * 
	 * @param authData
	 * @param eventData
	 * @return
	 * @throws HttpResponseException
	 * @throws IOException
	 */
	@Deprecated
	public HashMap<String, Attendee> uploadAttendeeData(Event eventData,
			HashMap<String, Attendee> attendeeDataList)
			throws HttpResponseException, IOException {

		HashMap<String, Attendee> result = null;

		String uri = mServerEntry.getBaseUrl() + URI_REGISTER_ATTENDEE_BASE
				+ eventData.getEventCode() + ".xml";
		List<NameValuePair> putContent = createPutContent(attendeeDataList);

		if (putContent.size() > 0) {

			HttpPut method = new HttpPut(uri);
			method.setEntity(new UrlEncodedFormEntity(putContent, HTTP.UTF_8));
			InputStream in = httpConnect(method);

			// ハンドラ
			UploadAttendeeHandler handler = new UploadAttendeeHandler();

			if (parseXml(handler, in)) {
				result = handler.mAttendeeList;
			}

		}

		return result;
	}

}
